1 Method

Raw isoform-level counts were obtained from lr-kallisto (v0.51.1) and imported into R as a counts matrix. To ensure robust statistical analysis, transcripts were filtered to retain only those with at least 10 counts in two or more samples.

Differential isoform expression analysis was performed using the DESeq2 (v1.44.0) package. The design matrix incorporated disease status (AD vs. CTRL) as the primary factor while adjusting for sex as a covariate.

Results were annotated by joining transcript IDs to gene symbols using a reference transcript-to-gene mapping file generated from GENCODE v48 human reference genome, transcriptome, and annotated GTF files. Significantly differentially expressed isoforms were filtered by adjusted p-value < 0.05 and fold-change threshold (log2 fold-change ≥ |log2(1.5)|). Volcano plots highlighting significant isoforms were generated in ggplot2 (3.5.3).

Significant isoforms were further explored by plotting normalized counts across conditions and stratified by sex to visualize expression differences. Finally, principal component analysis (PCA) on variance-stabilized transformed counts was performed to assess sample clustering by condition and covariates.

1.1 All analyses were conducted in R (v4.4.0) and tidyverse (v2.0.0) ecosystem packages.

library(AnnotationDbi)
## Loading required package: stats4
## Loading required package: BiocGenerics
## 
## Attaching package: 'BiocGenerics'
## The following objects are masked from 'package:stats':
## 
##     IQR, mad, sd, var, xtabs
## The following objects are masked from 'package:base':
## 
##     anyDuplicated, aperm, append, as.data.frame, basename, cbind,
##     colnames, dirname, do.call, duplicated, eval, evalq, Filter, Find,
##     get, grep, grepl, intersect, is.unsorted, lapply, Map, mapply,
##     match, mget, order, paste, pmax, pmax.int, pmin, pmin.int,
##     Position, rank, rbind, Reduce, rownames, sapply, setdiff, table,
##     tapply, union, unique, unsplit, which.max, which.min
## Loading required package: Biobase
## Welcome to Bioconductor
## 
##     Vignettes contain introductory material; view with
##     'browseVignettes()'. To cite Bioconductor, see
##     'citation("Biobase")', and for packages 'citation("pkgname")'.
## Loading required package: IRanges
## Loading required package: S4Vectors
## 
## Attaching package: 'S4Vectors'
## The following object is masked from 'package:utils':
## 
##     findMatches
## The following objects are masked from 'package:base':
## 
##     expand.grid, I, unname
library(DESeq2)
## Loading required package: GenomicRanges
## Loading required package: GenomeInfoDb
## Loading required package: SummarizedExperiment
## Loading required package: MatrixGenerics
## Loading required package: matrixStats
## 
## Attaching package: 'matrixStats'
## The following objects are masked from 'package:Biobase':
## 
##     anyMissing, rowMedians
## 
## Attaching package: 'MatrixGenerics'
## The following objects are masked from 'package:matrixStats':
## 
##     colAlls, colAnyNAs, colAnys, colAvgsPerRowSet, colCollapse,
##     colCounts, colCummaxs, colCummins, colCumprods, colCumsums,
##     colDiffs, colIQRDiffs, colIQRs, colLogSumExps, colMadDiffs,
##     colMads, colMaxs, colMeans2, colMedians, colMins, colOrderStats,
##     colProds, colQuantiles, colRanges, colRanks, colSdDiffs, colSds,
##     colSums2, colTabulates, colVarDiffs, colVars, colWeightedMads,
##     colWeightedMeans, colWeightedMedians, colWeightedSds,
##     colWeightedVars, rowAlls, rowAnyNAs, rowAnys, rowAvgsPerColSet,
##     rowCollapse, rowCounts, rowCummaxs, rowCummins, rowCumprods,
##     rowCumsums, rowDiffs, rowIQRDiffs, rowIQRs, rowLogSumExps,
##     rowMadDiffs, rowMads, rowMaxs, rowMeans2, rowMedians, rowMins,
##     rowOrderStats, rowProds, rowQuantiles, rowRanges, rowRanks,
##     rowSdDiffs, rowSds, rowSums2, rowTabulates, rowVarDiffs, rowVars,
##     rowWeightedMads, rowWeightedMeans, rowWeightedMedians,
##     rowWeightedSds, rowWeightedVars
## The following object is masked from 'package:Biobase':
## 
##     rowMedians
library(EnsDb.Hsapiens.v86)
## Loading required package: ensembldb
## Loading required package: GenomicFeatures
## Loading required package: AnnotationFilter
## 
## Attaching package: 'ensembldb'
## The following object is masked from 'package:stats':
## 
##     filter
library(ggrepel)
## Loading required package: ggplot2
library(knitr)
library(tidyverse)
## ── Attaching core tidyverse packages ──────────────────────── tidyverse 2.0.0 ──
## ✔ dplyr     1.1.4     ✔ readr     2.1.5
## ✔ forcats   1.0.0     ✔ stringr   1.5.1
## ✔ lubridate 1.9.3     ✔ tibble    3.2.1
## ✔ purrr     1.1.0     ✔ tidyr     1.3.1
## ── Conflicts ────────────────────────────────────────── tidyverse_conflicts() ──
## ✖ lubridate::%within%() masks IRanges::%within%()
## ✖ dplyr::collapse()     masks IRanges::collapse()
## ✖ dplyr::combine()      masks Biobase::combine(), BiocGenerics::combine()
## ✖ dplyr::count()        masks matrixStats::count()
## ✖ dplyr::desc()         masks IRanges::desc()
## ✖ tidyr::expand()       masks S4Vectors::expand()
## ✖ dplyr::filter()       masks ensembldb::filter(), stats::filter()
## ✖ dplyr::first()        masks S4Vectors::first()
## ✖ dplyr::lag()          masks stats::lag()
## ✖ ggplot2::Position()   masks BiocGenerics::Position(), base::Position()
## ✖ purrr::reduce()       masks GenomicRanges::reduce(), IRanges::reduce()
## ✖ dplyr::rename()       masks S4Vectors::rename()
## ✖ lubridate::second()   masks S4Vectors::second()
## ✖ lubridate::second<-() masks S4Vectors::second<-()
## ✖ dplyr::select()       masks ensembldb::select(), AnnotationDbi::select()
## ✖ dplyr::slice()        masks IRanges::slice()
## ℹ Use the conflicted package (<http://conflicted.r-lib.org/>) to force all conflicts to become errors
knitr::opts_chunk$set(echo=TRUE, warning=FALSE, message=FALSE, fig.width=12, fig.height=8, fig.align = "center")
sessionInfo()
## R version 4.4.0 (2024-04-24)
## Platform: x86_64-pc-linux-gnu
## Running under: AlmaLinux 8.10 (Cerulean Leopard)
## 
## Matrix products: default
## BLAS/LAPACK: FlexiBLAS NETLIB;  LAPACK version 3.11.0
## 
## locale:
##  [1] LC_CTYPE=en_US.UTF-8       LC_NUMERIC=C              
##  [3] LC_TIME=en_US.UTF-8        LC_COLLATE=en_US.UTF-8    
##  [5] LC_MONETARY=en_US.UTF-8    LC_MESSAGES=en_US.UTF-8   
##  [7] LC_PAPER=en_US.UTF-8       LC_NAME=C                 
##  [9] LC_ADDRESS=C               LC_TELEPHONE=C            
## [11] LC_MEASUREMENT=en_US.UTF-8 LC_IDENTIFICATION=C       
## 
## time zone: America/New_York
## tzcode source: system (glibc)
## 
## attached base packages:
## [1] stats4    stats     graphics  grDevices utils     datasets  methods  
## [8] base     
## 
## other attached packages:
##  [1] lubridate_1.9.3             forcats_1.0.0              
##  [3] stringr_1.5.1               dplyr_1.1.4                
##  [5] purrr_1.1.0                 readr_2.1.5                
##  [7] tidyr_1.3.1                 tibble_3.2.1               
##  [9] tidyverse_2.0.0             knitr_1.50                 
## [11] ggrepel_0.9.6               ggplot2_3.5.2              
## [13] EnsDb.Hsapiens.v86_2.99.0   ensembldb_2.28.0           
## [15] AnnotationFilter_1.28.0     GenomicFeatures_1.56.0     
## [17] DESeq2_1.44.0               SummarizedExperiment_1.34.0
## [19] MatrixGenerics_1.16.0       matrixStats_1.5.0          
## [21] GenomicRanges_1.56.0        GenomeInfoDb_1.40.0        
## [23] AnnotationDbi_1.66.0        IRanges_2.38.0             
## [25] S4Vectors_0.42.1            Biobase_2.64.0             
## [27] BiocGenerics_0.50.0        
## 
## loaded via a namespace (and not attached):
##  [1] DBI_1.2.2                bitops_1.0-9             rlang_1.1.6             
##  [4] magrittr_2.0.3           compiler_4.4.0           RSQLite_2.3.6           
##  [7] png_0.1-8                vctrs_0.6.5              ProtGenerics_1.36.0     
## [10] pkgconfig_2.0.3          crayon_1.5.3             fastmap_1.2.0           
## [13] XVector_0.44.0           Rsamtools_2.20.0         rmarkdown_2.29          
## [16] tzdb_0.4.0               UCSC.utils_1.0.0         bit_4.0.5               
## [19] xfun_0.52                zlibbioc_1.50.0          cachem_1.1.0            
## [22] jsonlite_2.0.0           blob_1.2.4               DelayedArray_0.30.1     
## [25] BiocParallel_1.38.0      parallel_4.4.0           R6_2.6.1                
## [28] bslib_0.9.0              stringi_1.8.3            RColorBrewer_1.1-3      
## [31] rtracklayer_1.64.0       jquerylib_0.1.4          Rcpp_1.1.0              
## [34] Matrix_1.7-0             timechange_0.3.0         tidyselect_1.2.1        
## [37] rstudioapi_0.16.0        dichromat_2.0-0.1        abind_1.4-8             
## [40] yaml_2.3.10              codetools_0.2-20         curl_6.4.0              
## [43] lattice_0.22-6           withr_3.0.2              KEGGREST_1.44.0         
## [46] evaluate_1.0.4           Biostrings_2.72.0        pillar_1.11.0           
## [49] generics_0.1.4           RCurl_1.98-1.14          hms_1.1.3               
## [52] scales_1.4.0             glue_1.8.0               lazyeval_0.2.2          
## [55] tools_4.4.0              BiocIO_1.14.0            locfit_1.5-9.9          
## [58] GenomicAlignments_1.40.0 XML_3.99-0.16.1          grid_4.4.0              
## [61] colorspace_2.1-1         GenomeInfoDbData_1.2.12  restfulr_0.0.15         
## [64] cli_3.6.5                S4Arrays_1.4.0           gtable_0.3.6            
## [67] sass_0.4.10              digest_0.6.37            SparseArray_1.4.0       
## [70] rjson_0.2.21             farver_2.1.2             memoise_2.0.1           
## [73] htmltools_0.5.8.1        lifecycle_1.0.4          httr_1.4.7              
## [76] bit64_4.0.5

2 Raw Counts

# read in raw counts matrix
counts <- read.csv("../kallisto_counts/kallisto_isoform_counts_all.csv")

counts

3 Filter Counts

Keep transcripts with at least 2 samples that have ≥ 10 counts

# filter counts to keep transcripts with at least 2 samples with counts >= 10

# all columns to integers
counts[ , -1] <- lapply(counts[ , -1], as.integer)

counts_filtered <- counts[rowSums(counts[-1] >= 10)>= 2, ]

counts_filtered

4 Run DESeq2

# create deseq2 object

# counts matrix
counts_matrix <- as.matrix(counts_filtered[-1])
row.names(counts_matrix) <- counts_filtered$isoform_id

4.1 Covariate Data for DESeq2

# col data
col_data <- tibble(
  sample_name = colnames(counts_filtered[-1])) %>%
  separate(sample_name,c("sample","condition"),sep="_",remove=FALSE) %>%
  mutate(condition=as.factor(condition))

# add other covariates for pca
col_data$sex <- factor(c('male','male','male','female','female','female','male','female'))
#col_data$race <- factor(c('Black','Black', 'White', 'White', 'White','White','Black','Black'))

col_data

4.2 Design Formula

# design matrix and dds object
design <- formula(~ condition + sex)
design
## ~condition + sex

5 Add Gene Names to Differential Expression Results

# add gene names

t2g_file <- readr::read_delim("../../refs/human.t2g", delim = "\t", col_names = c("transcript_id", "gene_id", "gene_symbol", "gene_version", "chr", "start", "end", "strand")) %>%
  dplyr::select(transcript_id, gene_symbol)

res_df <- res %>%
  as.data.frame() %>%
  arrange(padj) %>%
  tibble::rownames_to_column("isoform_id") %>%
  inner_join(t2g_file, by = join_by(isoform_id == transcript_id)) %>%
  dplyr::select(isoform_id, gene_symbol, everything())

res_df
write.csv(res_df, "kallisto_differential_isoform_expression.csv", row.names = FALSE)

6 Volcano Plot of Differentially Expressed Isoforms [Labelled with Gene Name]

Padj threshold = 0.05 Log2FC threshold = +/- 1.5

7 Individual Isoform Count Plots by Condition and Sex

# plot sample counts for significant differentially expressed isoforms

significant_isoforms <- res_df %>% 
  dplyr::filter(padj < 0.05) %>% 
  pull(isoform_id)

run_plot_counts <- function(dds, iso_id) {
  
  gene_name <- res_df %>% 
    dplyr::filter(res_df$isoform_id == iso_id) %>% 
    pull(gene_symbol)
  
  plot_data <- plotCounts(dds, iso_id, intgroup = c("condition", "sex"), returnData = TRUE)

  p <- plot_data %>%
    ggplot(aes(x=condition, y=count, color = sex)) + 
    geom_point(position=position_jitter(w=0.1,h=0)) + 
    ggtitle(paste0(gene_name, " (", iso_id, ")")) +
    theme_minimal()

  print(p)
}

for (isoform in significant_isoforms) {
  run_plot_counts(dds,isoform)
}

8 PCA on VST-Normalized Counts

# pca
vsd <- vst(dds, blind=FALSE)

plotPCA(vsd, intgroup=c("condition"))

plotPCA(vsd, intgroup=c("sex"))

plotPCA(vsd, intgroup=c("condition", "sex"))

LS0tCnRpdGxlOiAicGFjYmlvX2FkX3BpbG90X2RpZmZlcmVudGlhbF9pc29mb3JtX2V4cHJlc3Npb25fa2FsbGlzdG9fY291bnRzIgphdXRob3I6IFJhY2hlbCBCb3phZGppYW4KZGF0ZTogOC82LzI1Cm91dHB1dDogCiAgaHRtbF9kb2N1bWVudDoKICAgIGNvZGVfZm9sZGluZzogc2hvdwogICAgY29kZV9kb3dubG9hZDogdHJ1ZQogICAgZGZfcHJpbnQ6IHBhZ2VkCiAgICB0aGVtZTogeWV0aQogICAgaGlnaGxpZ2h0OiB0YW5nbwogICAgdG9jOiB5ZXMKICAgIHRvY19mbG9hdDoKICAgICAgY29sbGFwc2VkOiBmYWxzZQogICAgICBzbW9vdGhfc2Nyb2xsOiBmYWxzZQogICAgbnVtYmVyX3NlY3Rpb25zOiB0cnVlCi0tLQoKIyBNZXRob2QKClJhdyBpc29mb3JtLWxldmVsIGNvdW50cyB3ZXJlIG9idGFpbmVkIGZyb20gbHIta2FsbGlzdG8gKHYwLjUxLjEpIGFuZCBpbXBvcnRlZCBpbnRvIFIgYXMgYSBjb3VudHMgbWF0cml4LiBUbyBlbnN1cmUgcm9idXN0IHN0YXRpc3RpY2FsIGFuYWx5c2lzLCB0cmFuc2NyaXB0cyB3ZXJlIGZpbHRlcmVkIHRvIHJldGFpbiBvbmx5IHRob3NlIHdpdGggYXQgbGVhc3QgMTAgY291bnRzIGluIHR3byBvciBtb3JlIHNhbXBsZXMuCgpEaWZmZXJlbnRpYWwgaXNvZm9ybSBleHByZXNzaW9uIGFuYWx5c2lzIHdhcyBwZXJmb3JtZWQgdXNpbmcgdGhlIERFU2VxMiAodjEuNDQuMCkgcGFja2FnZS4gVGhlIGRlc2lnbiBtYXRyaXggaW5jb3Jwb3JhdGVkIGRpc2Vhc2Ugc3RhdHVzIChBRCB2cy4gQ1RSTCkgYXMgdGhlIHByaW1hcnkgZmFjdG9yIHdoaWxlIGFkanVzdGluZyBmb3Igc2V4IGFzIGEgY292YXJpYXRlLgoKUmVzdWx0cyB3ZXJlIGFubm90YXRlZCBieSBqb2luaW5nIHRyYW5zY3JpcHQgSURzIHRvIGdlbmUgc3ltYm9scyB1c2luZyBhIHJlZmVyZW5jZSB0cmFuc2NyaXB0LXRvLWdlbmUgbWFwcGluZyBmaWxlIGdlbmVyYXRlZCBmcm9tIEdFTkNPREUgdjQ4IGh1bWFuIHJlZmVyZW5jZSBnZW5vbWUsIHRyYW5zY3JpcHRvbWUsIGFuZCBhbm5vdGF0ZWQgR1RGIGZpbGVzLiBTaWduaWZpY2FudGx5IGRpZmZlcmVudGlhbGx5IGV4cHJlc3NlZCBpc29mb3JtcyB3ZXJlIGZpbHRlcmVkIGJ5IGFkanVzdGVkIHAtdmFsdWUgXDwgMC4wNSBhbmQgZm9sZC1jaGFuZ2UgdGhyZXNob2xkIChsb2cyIGZvbGQtY2hhbmdlIOKJpSBcfGxvZzIoMS41KVx8KS4gVm9sY2FubyBwbG90cyBoaWdobGlnaHRpbmcgc2lnbmlmaWNhbnQgaXNvZm9ybXMgd2VyZSBnZW5lcmF0ZWQgaW4gZ2dwbG90MiAoMy41LjMpLgoKU2lnbmlmaWNhbnQgaXNvZm9ybXMgd2VyZSBmdXJ0aGVyIGV4cGxvcmVkIGJ5IHBsb3R0aW5nIG5vcm1hbGl6ZWQgY291bnRzIGFjcm9zcyBjb25kaXRpb25zIGFuZCBzdHJhdGlmaWVkIGJ5IHNleCB0byB2aXN1YWxpemUgZXhwcmVzc2lvbiBkaWZmZXJlbmNlcy4gRmluYWxseSwgcHJpbmNpcGFsIGNvbXBvbmVudCBhbmFseXNpcyAoUENBKSBvbiB2YXJpYW5jZS1zdGFiaWxpemVkIHRyYW5zZm9ybWVkIGNvdW50cyB3YXMgcGVyZm9ybWVkIHRvIGFzc2VzcyBzYW1wbGUgY2x1c3RlcmluZyBieSBjb25kaXRpb24gYW5kIGNvdmFyaWF0ZXMuCgojIyBBbGwgYW5hbHlzZXMgd2VyZSBjb25kdWN0ZWQgaW4gUiAodjQuNC4wKSBhbmQgdGlkeXZlcnNlICh2Mi4wLjApIGVjb3N5c3RlbSBwYWNrYWdlcy4KCmBgYHtyfQpsaWJyYXJ5KEFubm90YXRpb25EYmkpCmxpYnJhcnkoREVTZXEyKQpsaWJyYXJ5KEVuc0RiLkhzYXBpZW5zLnY4NikKbGlicmFyeShnZ3JlcGVsKQpsaWJyYXJ5KGtuaXRyKQpsaWJyYXJ5KHRpZHl2ZXJzZSkKCmtuaXRyOjpvcHRzX2NodW5rJHNldChlY2hvPVRSVUUsIHdhcm5pbmc9RkFMU0UsIG1lc3NhZ2U9RkFMU0UsIGZpZy53aWR0aD0xMiwgZmlnLmhlaWdodD04LCBmaWcuYWxpZ24gPSAiY2VudGVyIikKYGBgCgpgYGB7cn0Kc2Vzc2lvbkluZm8oKQpgYGAKCiMgUmF3IENvdW50cwoKYGBge3J9CiMgcmVhZCBpbiByYXcgY291bnRzIG1hdHJpeApjb3VudHMgPC0gcmVhZC5jc3YoIi4uL2thbGxpc3RvX2NvdW50cy9rYWxsaXN0b19pc29mb3JtX2NvdW50c19hbGwuY3N2IikKCmNvdW50cwpgYGAKCiMgRmlsdGVyIENvdW50cwoKS2VlcCB0cmFuc2NyaXB0cyB3aXRoIGF0IGxlYXN0IDIgc2FtcGxlcyB0aGF0IGhhdmUg4omlIDEwIGNvdW50cwoKYGBge3J9CiMgZmlsdGVyIGNvdW50cyB0byBrZWVwIHRyYW5zY3JpcHRzIHdpdGggYXQgbGVhc3QgMiBzYW1wbGVzIHdpdGggY291bnRzID49IDEwCgojIGFsbCBjb2x1bW5zIHRvIGludGVnZXJzCmNvdW50c1sgLCAtMV0gPC0gbGFwcGx5KGNvdW50c1sgLCAtMV0sIGFzLmludGVnZXIpCgpjb3VudHNfZmlsdGVyZWQgPC0gY291bnRzW3Jvd1N1bXMoY291bnRzWy0xXSA+PSAxMCk+PSAyLCBdCgpjb3VudHNfZmlsdGVyZWQKYGBgCgojIFJ1biBERVNlcTIKCmBgYHtyfQojIGNyZWF0ZSBkZXNlcTIgb2JqZWN0CgojIGNvdW50cyBtYXRyaXgKY291bnRzX21hdHJpeCA8LSBhcy5tYXRyaXgoY291bnRzX2ZpbHRlcmVkWy0xXSkKcm93Lm5hbWVzKGNvdW50c19tYXRyaXgpIDwtIGNvdW50c19maWx0ZXJlZCRpc29mb3JtX2lkCmBgYAoKIyMgQ292YXJpYXRlIERhdGEgZm9yIERFU2VxMgoKYGBge3J9CiMgY29sIGRhdGEKY29sX2RhdGEgPC0gdGliYmxlKAogIHNhbXBsZV9uYW1lID0gY29sbmFtZXMoY291bnRzX2ZpbHRlcmVkWy0xXSkpICU+JQogIHNlcGFyYXRlKHNhbXBsZV9uYW1lLGMoInNhbXBsZSIsImNvbmRpdGlvbiIpLHNlcD0iXyIscmVtb3ZlPUZBTFNFKSAlPiUKICBtdXRhdGUoY29uZGl0aW9uPWFzLmZhY3Rvcihjb25kaXRpb24pKQoKIyBhZGQgb3RoZXIgY292YXJpYXRlcyBmb3IgcGNhCmNvbF9kYXRhJHNleCA8LSBmYWN0b3IoYygnbWFsZScsJ21hbGUnLCdtYWxlJywnZmVtYWxlJywnZmVtYWxlJywnZmVtYWxlJywnbWFsZScsJ2ZlbWFsZScpKQojY29sX2RhdGEkcmFjZSA8LSBmYWN0b3IoYygnQmxhY2snLCdCbGFjaycsICdXaGl0ZScsICdXaGl0ZScsICdXaGl0ZScsJ1doaXRlJywnQmxhY2snLCdCbGFjaycpKQoKY29sX2RhdGEKYGBgCgojIyBEZXNpZ24gRm9ybXVsYQoKYGBge3J9CiMgZGVzaWduIG1hdHJpeCBhbmQgZGRzIG9iamVjdApkZXNpZ24gPC0gZm9ybXVsYSh+IGNvbmRpdGlvbiArIHNleCkKZGVzaWduCmBgYAoKYGBge3IsIGVjaG8gPSBGQUxTRX0KIyBydW4gZGVzZXEyCmRkcyA8LSBERVNlcURhdGFTZXRGcm9tTWF0cml4KAogIGNvdW50RGF0YT1jb3VudHNfbWF0cml4LAogIGNvbERhdGE9Y29sX2RhdGEsCiAgZGVzaWduPWRlc2lnbgopCmRkcyA8LSBERVNlcShkZHMpCnJlcyA8LSByZXN1bHRzKGRkcykKYGBgCgojIEFkZCBHZW5lIE5hbWVzIHRvIERpZmZlcmVudGlhbCBFeHByZXNzaW9uIFJlc3VsdHMKCmBgYHtyfQojIGFkZCBnZW5lIG5hbWVzCgp0MmdfZmlsZSA8LSByZWFkcjo6cmVhZF9kZWxpbSgiLi4vLi4vcmVmcy9odW1hbi50MmciLCBkZWxpbSA9ICJcdCIsIGNvbF9uYW1lcyA9IGMoInRyYW5zY3JpcHRfaWQiLCAiZ2VuZV9pZCIsICJnZW5lX3N5bWJvbCIsICJnZW5lX3ZlcnNpb24iLCAiY2hyIiwgInN0YXJ0IiwgImVuZCIsICJzdHJhbmQiKSkgJT4lCiAgZHBseXI6OnNlbGVjdCh0cmFuc2NyaXB0X2lkLCBnZW5lX3N5bWJvbCkKCnJlc19kZiA8LSByZXMgJT4lCiAgYXMuZGF0YS5mcmFtZSgpICU+JQogIGFycmFuZ2UocGFkaikgJT4lCiAgdGliYmxlOjpyb3duYW1lc190b19jb2x1bW4oImlzb2Zvcm1faWQiKSAlPiUKICBpbm5lcl9qb2luKHQyZ19maWxlLCBieSA9IGpvaW5fYnkoaXNvZm9ybV9pZCA9PSB0cmFuc2NyaXB0X2lkKSkgJT4lCiAgZHBseXI6OnNlbGVjdChpc29mb3JtX2lkLCBnZW5lX3N5bWJvbCwgZXZlcnl0aGluZygpKQoKcmVzX2RmCndyaXRlLmNzdihyZXNfZGYsICJrYWxsaXN0b19kaWZmZXJlbnRpYWxfaXNvZm9ybV9leHByZXNzaW9uLmNzdiIsIHJvdy5uYW1lcyA9IEZBTFNFKQpgYGAKCiMgVm9sY2FubyBQbG90IG9mIERpZmZlcmVudGlhbGx5IEV4cHJlc3NlZCBJc29mb3JtcyBbTGFiZWxsZWQgd2l0aCBHZW5lIE5hbWVdCgpQYWRqIHRocmVzaG9sZCA9IDAuMDUgTG9nMkZDIHRocmVzaG9sZCA9ICsvLSAxLjUKCmBgYHtyLCBlY2hvID0gRkFMU0V9CnBhZGpfdGhyZXNob2xkIDwtIDAuMDUKbG9nMmZjX3RocmVzaG9sZCA8LSBhYnMobG9nMigxLjUpKQoKIyBHZXQgZ2VuZXMgdG8gbGFiZWwgKHNpZ25pZmljYW50ICYgYWJvdmUgRkMgdGhyZXNob2xkKQpnZW5lc190b19sYWJlbCA8LSByZXNfZGYgJT4lCiAgZHBseXI6OmZpbHRlcihwYWRqIDw9IHBhZGpfdGhyZXNob2xkICYgYWJzKGxvZzJGb2xkQ2hhbmdlKSA+PSBsb2cyZmNfdGhyZXNob2xkKSAlPiUKICBwdWxsKGlzb2Zvcm1faWQpCgojIENyZWF0ZSBnZW5lX2xhYmVsIGNvbHVtbgpyZXNfZGYgPC0gcmVzX2RmICU+JQogIG11dGF0ZShnZW5lX2xhYmVsID0gaWZfZWxzZShpc29mb3JtX2lkICVpbiUgZ2VuZXNfdG9fbGFiZWwsIGdlbmVfc3ltYm9sLCBOQV9jaGFyYWN0ZXJfKSkKCiMgTWFrZSB2b2xjYW5vIHBsb3QKdm9sY2Fub19wbG90IDwtIHJlc19kZiAlPiUKICBnZ3Bsb3QoYWVzKHggPSBsb2cyRm9sZENoYW5nZSwgeSA9IC1sb2cxMChwYWRqKSkpICsKICBnZW9tX3BvaW50KGNvbG9yID0gImdyZXkiKSArCiAgZ2VvbV9wb2ludChkYXRhID0gc3Vic2V0KHJlc19kZiwgcGFkaiA8PSBwYWRqX3RocmVzaG9sZCAmIGxvZzJGb2xkQ2hhbmdlID49IGxvZzJmY190aHJlc2hvbGQpLCBjb2xvciA9ICJyZWQiKSArCiAgZ2VvbV9wb2ludChkYXRhID0gc3Vic2V0KHJlc19kZiwgcGFkaiA8PSBwYWRqX3RocmVzaG9sZCAmIGxvZzJGb2xkQ2hhbmdlIDw9IC1sb2cyZmNfdGhyZXNob2xkKSwgY29sb3IgPSAiYmx1ZSIpICsKICBnZW9tX3RleHRfcmVwZWwoCiAgICBkYXRhID0gc3Vic2V0KHJlc19kZiwgIWlzLm5hKGdlbmVfbGFiZWwpKSwKICAgIGFlcyhsYWJlbCA9IGdlbmVfbGFiZWwpLAogICAgbWF4Lm92ZXJsYXBzID0gSW5mLAogICAgYm94LnBhZGRpbmcgPSAwLjcsCiAgICBwb2ludC5wYWRkaW5nID0gMC4yNQogICkgKwogIGdlb21faGxpbmUoeWludGVyY2VwdCA9IC1sb2cxMChwYWRqX3RocmVzaG9sZCksIGxpbmV0eXBlID0gImxvbmdkYXNoIiwgY29sb3VyID0gImdyZXkiKSArCiAgZ2VvbV92bGluZSh4aW50ZXJjZXB0ID0gbG9nMmZjX3RocmVzaG9sZCwgbGluZXR5cGUgPSAibG9uZ2Rhc2giLCBjb2xvdXIgPSAicmVkIikgKwogIGdlb21fdmxpbmUoeGludGVyY2VwdCA9IC1sb2cyZmNfdGhyZXNob2xkLCBsaW5ldHlwZSA9ICJsb25nZGFzaCIsIGNvbG91ciA9ICJibHVlIikgKwogIGxhYnMoCiAgICB0aXRsZSA9ICJWb2xjYW5vIFBsb3Qgb2YgRGlmZmVyZW50aWFsbHkgRXhwcmVzc2VkIElzb2Zvcm1zIFtHZW5lIFN5bWJvbHNdIiwKICAgIHggPSAiTG9nMiBGb2xkIENoYW5nZSIsCiAgICB5ID0gIi1Mb2cxMChwYWRqKSIKICApICsKICB0aGVtZV9idygpICsKICB0aGVtZShwbG90LnRpdGxlID0gZWxlbWVudF90ZXh0KGhqdXN0ID0gMC41KSkKCnZvbGNhbm9fcGxvdAoKcmVzX2RmICU+JSBkcGx5cjo6ZmlsdGVyKCFpcy5uYShnZW5lX2xhYmVsKSkKYGBgCgojIEluZGl2aWR1YWwgSXNvZm9ybSBDb3VudCBQbG90cyBieSBDb25kaXRpb24gYW5kIFNleAoKYGBge3J9CiMgcGxvdCBzYW1wbGUgY291bnRzIGZvciBzaWduaWZpY2FudCBkaWZmZXJlbnRpYWxseSBleHByZXNzZWQgaXNvZm9ybXMKCnNpZ25pZmljYW50X2lzb2Zvcm1zIDwtIHJlc19kZiAlPiUgCiAgZHBseXI6OmZpbHRlcihwYWRqIDwgMC4wNSkgJT4lIAogIHB1bGwoaXNvZm9ybV9pZCkKCnJ1bl9wbG90X2NvdW50cyA8LSBmdW5jdGlvbihkZHMsIGlzb19pZCkgewogIAogIGdlbmVfbmFtZSA8LSByZXNfZGYgJT4lIAogICAgZHBseXI6OmZpbHRlcihyZXNfZGYkaXNvZm9ybV9pZCA9PSBpc29faWQpICU+JSAKICAgIHB1bGwoZ2VuZV9zeW1ib2wpCiAgCiAgcGxvdF9kYXRhIDwtIHBsb3RDb3VudHMoZGRzLCBpc29faWQsIGludGdyb3VwID0gYygiY29uZGl0aW9uIiwgInNleCIpLCByZXR1cm5EYXRhID0gVFJVRSkKCiAgcCA8LSBwbG90X2RhdGEgJT4lCiAgICBnZ3Bsb3QoYWVzKHg9Y29uZGl0aW9uLCB5PWNvdW50LCBjb2xvciA9IHNleCkpICsgCiAgICBnZW9tX3BvaW50KHBvc2l0aW9uPXBvc2l0aW9uX2ppdHRlcih3PTAuMSxoPTApKSArIAogICAgZ2d0aXRsZShwYXN0ZTAoZ2VuZV9uYW1lLCAiICgiLCBpc29faWQsICIpIikpICsKICAgIHRoZW1lX21pbmltYWwoKQoKICBwcmludChwKQp9Cgpmb3IgKGlzb2Zvcm0gaW4gc2lnbmlmaWNhbnRfaXNvZm9ybXMpIHsKICBydW5fcGxvdF9jb3VudHMoZGRzLGlzb2Zvcm0pCn0KYGBgCgojIFBDQSBvbiBWU1QtTm9ybWFsaXplZCBDb3VudHMKCmBgYHtyfQojIHBjYQp2c2QgPC0gdnN0KGRkcywgYmxpbmQ9RkFMU0UpCgpwbG90UENBKHZzZCwgaW50Z3JvdXA9YygiY29uZGl0aW9uIikpCnBsb3RQQ0EodnNkLCBpbnRncm91cD1jKCJzZXgiKSkKCnBsb3RQQ0EodnNkLCBpbnRncm91cD1jKCJjb25kaXRpb24iLCAic2V4IikpCmBgYAo=